home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 005 / tsrcom16.arc / MAPMEM.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-03-24  |  18.0 KB  |  575 lines

  1. {**************************************************************************
  2. *   Maps system memory blocks for MS/PCDOS 2.0 and higher.                *
  3. *   Also maps expanded memory allocation blocks                           *
  4. *   Copyright (c) 1986 Kim Kokkonen, TurboPower Software.                 *
  5. *   Released to the public domain for personal, non-commercial use only.  *
  6. ***************************************************************************
  7. *   written 1/2/86                                                        *
  8. *   revised 1/10/86 for                                                   *
  9. *     running under DOS 2.X, where block owner names are unknown          *
  10. *   revised 1/22/86 for                                                   *
  11. *     a bug in parsing the owner name of the block                        *
  12. *     a quirk in the way that the DOS PRINT buffer installs itself        *
  13. *     minor cosmetic changes                                              *
  14. *   revised 2/6/86 for (version 1.3)                                      *
  15. *     smarter filtering for processes that deallocate their environment   *
  16. *   revised 2/23/86 for (version 1.4)                                     *
  17. *     add a map of Expanded memory (EMS) as well                          *
  18. *   revised 2/26/86 for (version 1.5)                                     *
  19. *     change format of last memory block                                  *
  20. *     change to more reliable scheme of finding first block               *
  21. *       (thanks to Chris Dunford for pointing out a useful                *
  22. *        undocumented DOS function).                                      *
  23. *     support environment lengths up to 32K                               *
  24. *   revised 3/8/86 for (version 1.6)                                      *
  25. *     support "verbose" output mode                                       *
  26. *       display open file handles                                         *
  27. *       show command line of each block                                   *
  28. ***************************************************************************
  29. *   telephone: 408-378-3672, CompuServe: 72457,2131.                      *
  30. *   requires Turbo version 3 to compile.                                  *
  31. *   Compile with mAx dynamic memory = FFFF.                               *
  32. ***************************************************************************}
  33.  
  34. {$P128}
  35.  
  36. PROGRAM MapMem;
  37.   {-look at the system memory map using DOS memory control blocks}
  38. CONST
  39.   Version = '1.6';
  40.   MaxBlocks = 100;            {max number of DOS memory blocks checked}
  41.   MaxVector = $FF;            {highest interrupt vector checked for trapping}
  42. TYPE
  43.   Block =
  44.   RECORD                      {store info about each memory block as it is found}
  45.     idbyte : Byte;
  46.     mcb : Integer;
  47.     psp : Integer;
  48.     len : Integer;
  49.     psplen : Integer;
  50.     env : Integer;
  51.     cnt : Integer;
  52.   END;
  53.   BlockType = 0..MaxBlocks;
  54.   BlockArray = ARRAY[BlockType] OF Block;
  55.   Pathname = STRING[64];
  56.   registers =
  57.   RECORD
  58.     CASE Integer OF
  59.       1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  60.       2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  61.   END;
  62.  
  63. VAR
  64.   Blocks : BlockArray;
  65.   BlockNum : BlockType;
  66.   verbose : Boolean;
  67.  
  68.   PROCEDURE StripNonAscii(VAR t : Pathname);
  69.     {-return an empty string if t contains any non-printable characters}
  70.   VAR
  71.     ipos : Byte;
  72.     goodname : Boolean;
  73.   BEGIN
  74.     goodname := True;
  75.     FOR ipos := 1 TO Length(t) DO
  76.       IF (t[ipos] < ' ') OR (t[ipos] > '}') THEN
  77.         goodname := False;
  78.     IF NOT(goodname) THEN t := '';
  79.   END {stripnonascii} ;
  80.  
  81.   PROCEDURE FindTheBlocks;
  82.     {-scan memory for the allocated memory blocks}
  83.   CONST
  84.     MidBlockID = $4D;         {byte DOS uses to identify part of MCB chain}
  85.     EndBlockID = $5A;         {byte DOS uses to identify last block of MCB chain}
  86.   VAR
  87.     mcbSeg : Integer;         {segment address of current MCB}
  88.     nextSeg : Integer;        {computed segment address for the next MCB}
  89.     gotFirst : Boolean;       {true after first MCB is found}
  90.     gotLast : Boolean;        {true after last MCB is found}
  91.     idbyte : Byte;            {byte that DOS uses to identify an MCB}
  92.  
  93.     FUNCTION GetStartMCB : Integer;
  94.       {-return the first MCB segment}
  95.     VAR
  96.       reg : registers;
  97.     BEGIN
  98.       reg.ah := $52;
  99.       MsDos(reg);
  100.       GetStartMCB := MemW[reg.es:(reg.bx-2)];
  101.     END {getstartmcb} ;
  102.  
  103.     PROCEDURE StoreTheBlock(VAR mcbSeg, nextSeg : Integer;
  104.                             VAR gotFirst, gotLast : Boolean);
  105.       {-store information regarding the memory block}
  106.     VAR
  107.       nextID : Byte;
  108.       pspAdd : Integer;       {segment address of the current PSP}
  109.       mcbLen : Integer;       {size of the current memory block in paragraphs}
  110.  
  111.     BEGIN
  112.  
  113.       mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs}
  114.       nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be}
  115.       pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB}
  116.       nextID := Mem[nextSeg:0];
  117.  
  118.       IF gotLast OR (nextID = EndBlockID) OR (nextID = MidBlockID) THEN BEGIN
  119.         BlockNum := Succ(BlockNum);
  120.         gotFirst := True;
  121.         WITH Blocks[BlockNum] DO BEGIN
  122.           idbyte := Mem[mcbSeg:0];
  123.           mcb := mcbSeg;
  124.           psp := pspAdd;
  125.           env := MemW[pspAdd:$2C];
  126.           len := mcbLen;
  127.           psplen := 0;
  128.           cnt := 1;
  129.         END;
  130.       END;
  131.  
  132.     END {storetheblock} ;
  133.  
  134.   BEGIN
  135.  
  136.     {initialize}
  137.     mcbSeg := GetStartMCB;
  138.     gotFirst := False;
  139.     gotLast := False;
  140.     BlockNum := 0;
  141.  
  142.     {scan all memory until the last block is found}
  143.     REPEAT
  144.       idbyte := Mem[mcbSeg:0];
  145.       IF idbyte = MidBlockID THEN BEGIN
  146.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  147.         IF gotFirst THEN mcbSeg := nextSeg ELSE mcbSeg := Succ(mcbSeg);
  148.       END ELSE IF gotFirst AND (idbyte = EndBlockID) THEN BEGIN
  149.         gotLast := True;
  150.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  151.       END ELSE BEGIN
  152.         {start block was invalid}
  153.         WriteLn('corrupted allocation chain or program error');
  154.         Halt(1);
  155.       END;
  156.     UNTIL gotLast;
  157.  
  158.   END {findtheblocks} ;
  159.  
  160.   FUNCTION Cardinal(i : Integer) : Real;
  161.     {-return an unsigned integer 0..65535}
  162.   BEGIN
  163.     Cardinal := 256.0*Hi(i)+Lo(i);
  164.   END {cardinal} ;
  165.  
  166.   PROCEDURE ShowTheBlocks;
  167.     {-analyze and display the blocks found}
  168.   TYPE
  169.     HexString = STRING[4];
  170.     Address = RECORD
  171.                 offset, segment : Integer;
  172.               END;
  173.     VectorType = 0..MaxVector;
  174.   VAR
  175.     st : Pathname;
  176.     b : BlockType;
  177.     StLen, DOSv : Byte;
  178.     Vectors : ARRAY[VectorType] OF Address ABSOLUTE 0 : 0;
  179.     Vtable : ARRAY[VectorType] OF Real;
  180.     SumNum : BlockType;
  181.     Sum : BlockArray;
  182.  
  183.     FUNCTION Hex(i : Integer) : HexString;
  184.       {-return hex representation of integer}
  185.     CONST
  186.       hc : ARRAY[0..15] OF Char = '0123456789ABCDEF';
  187.     VAR
  188.       l, h : Byte;
  189.     BEGIN
  190.       l := Lo(i); h := Hi(i);
  191.       Hex := hc[h SHR 4]+hc[h AND $F]+hc[l SHR 4]+hc[l AND $F];
  192.     END {hex} ;
  193.  
  194.     FUNCTION DOSversion : Byte;
  195.       {-return the major version number of DOS}
  196.     VAR
  197.       reg : registers;
  198.     BEGIN
  199.       reg.ah := $30;
  200.       MsDos(reg);
  201.       DOSversion := reg.al;
  202.     END {dosversion} ;
  203.  
  204.     FUNCTION Owner(startadd : Integer) : Pathname;
  205.       {-return the name of the owner program of an MCB}
  206.     TYPE
  207.       chararray = ARRAY[0..32767] OF Char;
  208.     VAR
  209.       e : ^chararray;
  210.       i : Integer;
  211.       t : Pathname;
  212.  
  213.       FUNCTION LongPos(m : Pathname; VAR s : chararray) : Integer;
  214.         {-return the position number of m in s, or 0 if not found}
  215.       VAR
  216.         mc : Char;
  217.         ss : Pathname;
  218.         i, maxindex : Integer;
  219.         found : Boolean;
  220.       BEGIN
  221.         i := 0;
  222.         maxindex := SizeOf(s)-Length(m);
  223.         ss[0] := m[0];
  224.         IF Length(m) > 0 THEN BEGIN
  225.           mc := m[1];
  226.           REPEAT
  227.             WHILE (s[i] <> mc) AND (i <= maxindex) DO
  228.               i := Succ(i);
  229.             IF s[i] = mc THEN BEGIN
  230.               Move(s[i], ss[1], Length(m));
  231.               found := (ss = m);
  232.               IF NOT(found) THEN i := Succ(i);
  233.             END;
  234.           UNTIL found OR (i > maxindex);
  235.           IF NOT(found) THEN i := 0;
  236.         END;
  237.         LongPos := i;
  238.       END {longpos} ;
  239.  
  240.       PROCEDURE StripPathname(VAR pname : Pathname);
  241.         {-remove leading drive or path name from the input}
  242.       VAR
  243.         spos, cpos, rpos : Byte;
  244.       BEGIN
  245.         spos := Pos('\', pname);
  246.         cpos := Pos(':', pname);
  247.         IF spos+cpos = 0 THEN Exit;
  248.         IF spos <> 0 THEN BEGIN
  249.           {find the last slash in the pathname}
  250.           rpos := Length(pname);
  251.           WHILE (rpos > 0) AND (pname[rpos] <> '\') DO rpos := Pred(rpos);
  252.         END ELSE
  253.           rpos := cpos;
  254.         Delete(pname, 1, rpos);
  255.       END {strippathname} ;
  256.  
  257.       PROCEDURE StripExtension(VAR pname : Pathname);
  258.         {-remove the file extension}
  259.       VAR
  260.         dotpos : Byte;
  261.       BEGIN
  262.         dotpos := Pos('.', pname);
  263.         IF dotpos <> 0 THEN
  264.           Delete(pname, dotpos, 64);
  265.       END {stripextension} ;
  266.  
  267.     BEGIN
  268.       {point to the environment string}
  269.       e := Ptr(startadd, 0);
  270.  
  271.       {find end of the standard environment}
  272.       i := LongPos(#0#0, e^);
  273.       IF i = 0 THEN BEGIN
  274.         {something's wrong, exit gracefully}
  275.         Owner := '';
  276.         Exit;
  277.       END;
  278.  
  279.       {end of environment found, get the program name that follows it}
  280.       t := '';
  281.       i := i+4;               {skip over #0#0#args}
  282.       REPEAT
  283.         t := t+e^[i];
  284.         i := Succ(i);
  285.       UNTIL (Length(t) > 64) OR (e^[i] = #0);
  286.  
  287.       StripNonAscii(t);
  288.       IF t = '' THEN
  289.         Owner := 'N/A'
  290.       ELSE BEGIN
  291.         StripPathname(t);
  292.         StripExtension(t);
  293.         IF t = '' THEN t := 'N/A';
  294.         Owner := t;
  295.       END;
  296.  
  297.     END {owner} ;
  298.  
  299.     PROCEDURE InitVectorTable;
  300.       {-build real equivalent of vector addresses}
  301.     VAR
  302.       v : VectorType;
  303.  
  304.       FUNCTION RealAdd(a : Address) : Real;
  305.         {-return the real equivalent of an address (pointer)}
  306.       BEGIN
  307.         WITH a DO
  308.           RealAdd := 16.0*Cardinal(segment)+Cardinal(offset);
  309.       END {realadd} ;
  310.  
  311.     BEGIN
  312.       FOR v := 0 TO MaxVector DO
  313.         Vtable[v] := RealAdd(Vectors[v]);
  314.     END {initvectortable} ;
  315.  
  316.     PROCEDURE WriteHooks(start, stop : Integer);
  317.       {-show the trapped interrupt vectors}
  318.     VAR
  319.       v : VectorType;
  320.       sadd, eadd : Real;
  321.     BEGIN
  322.       sadd := 16.0*Cardinal(start);
  323.       eadd := 16.0*Cardinal(stop);
  324.       FOR v := 0 TO MaxVector DO BEGIN
  325.         IF (Vtable[v] >= sadd) AND (Vtable[v] <= eadd) THEN
  326.           Write(Copy(Hex(v), 3, 2), ' ');
  327.       END;
  328.     END {writehooks} ;
  329.  
  330.     PROCEDURE SortByPSP(VAR Blocks : BlockArray; BlockNum : BlockType);
  331.       {-sort in order of ascending PSP}
  332.     VAR
  333.       i, j : BlockType;
  334.       temp : Block;
  335.     BEGIN
  336.       FOR i := 1 TO Pred(BlockNum) DO
  337.         FOR j := BlockNum DOWNTO Succ(i) DO
  338.           IF Cardinal(Blocks[j].psp) < Cardinal(Blocks[Pred(j)].psp) THEN BEGIN
  339.             temp := Blocks[j];
  340.             Blocks[j] := Blocks[Pred(j)];
  341.             Blocks[Pred(j)] := temp;
  342.           END;
  343.     END {SortByPSP} ;
  344.  
  345.     PROCEDURE SumTheBlocks(VAR Blocks : BlockArray;
  346.                            BlockNum : BlockType;
  347.                            VAR Sum : BlockArray;
  348.                            VAR SumNum : BlockType);
  349.       {-combine the blocks with equivalent PSPs}
  350.     VAR
  351.       prevPSP : Integer;
  352.       b : BlockType;
  353.     BEGIN
  354.       SumNum := 0;
  355.       prevPSP := 0;
  356.       FOR b := 1 TO BlockNum DO BEGIN
  357.         IF Blocks[b].psp <> prevPSP THEN BEGIN
  358.           SumNum := Succ(SumNum);
  359.           Sum[SumNum] := Blocks[b];
  360.           prevPSP := Blocks[b].psp;
  361.           IF prevPSP = CSeg THEN
  362.             {don't include the environment as part of free block's length}
  363.             Sum[SumNum].len := 0;
  364.         END ELSE
  365.           WITH Sum[SumNum] DO BEGIN
  366.             cnt := Succ(cnt);
  367.             len := len+Blocks[b].len;
  368.           END;
  369.         {get length of the block which owns the executable program}
  370.         {for checking vector trapping next}
  371.         IF Succ(Blocks[b].mcb) = Blocks[b].psp THEN
  372.           Sum[SumNum].psplen := Blocks[b].len;
  373.       END;
  374.     END {sumblocks} ;
  375.  
  376.     FUNCTION OpenHandles(psp : Integer) : Integer;
  377.       {-return the number of open handles owned by a process}
  378.     VAR
  379.       h, o : Integer;
  380.       b : Byte;
  381.     BEGIN
  382.       h := 0;
  383.       IF psp <> 8 THEN
  384.         FOR o := 0 TO 19 DO BEGIN
  385.           b := Mem[psp:$18+o];
  386.           IF NOT(b IN [$FF, 0..5]) THEN
  387.             h := Succ(h);
  388.         END;
  389.       OpenHandles := h;
  390.     END {openhandles} ;
  391.  
  392.     FUNCTION CommandLine(psp : Integer) : Pathname;
  393.       {-return the command line of the PSP}
  394.     VAR
  395.       t : Pathname;
  396.     BEGIN
  397.       IF psp <> 8 THEN BEGIN
  398.         Move(Mem[psp:$80], t, 65);
  399.         StripNonASCII(t);
  400.         IF t[0] > #64 THEN t[0] := #64;
  401.         WHILE (Length(t) > 0) AND (t[1] = #32) DO Delete(t, 1, 1)
  402.       END ELSE
  403.         t := '';
  404.       CommandLine := t;
  405.     END;
  406.  
  407.   BEGIN
  408.     WriteLn;
  409.     Write('Allocated Memory Map - by TurboPower Software - Version ', Version);
  410.  
  411.     IF verbose THEN BEGIN
  412.       WriteLn('  (verbose)');
  413.       WriteLn;
  414.       WriteLn(' PSP   MCB  files bytes  owner    command line  hooked vectors');
  415.       WriteLn('----- ----- ----- ------ -------- ------------- -----------------------------');
  416.     END ELSE BEGIN
  417.       WriteLn;
  418.       WriteLn;
  419.       WriteLn('  PSP   bytes  owner    command line        hooked vectors');
  420.       WriteLn('------- ------ -------- ------------------- ------------------------------');
  421.     END;
  422.  
  423.     DOSv := DOSversion;
  424.     InitVectorTable;
  425.     SortByPSP(Blocks, BlockNum);
  426.     SumTheBlocks(Blocks, BlockNum, Sum, SumNum);
  427.  
  428.     FOR b := 1 TO SumNum DO WITH Sum[b] DO BEGIN
  429.  
  430.       Write(' ', Hex(psp), '  ');        {PSP address}
  431.       IF verbose THEN BEGIN
  432.         Write(Hex(mcb), '  ',       {MCB address}
  433.         OpenHandles(psp):2, '  '); {number of open file handles}
  434.       END;
  435.       Write(16.0*Cardinal(len):6:0, '  '); {size of block in bytes}
  436.  
  437.       {get the program owning this block by scanning the environment}
  438.       IF b = SumNum THEN
  439.         st := 'free'
  440.       ELSE IF (DOSv >= 3) AND (cnt > 1) THEN
  441.         st := Owner(env)
  442.       ELSE
  443.         st := 'N/A';
  444.       WHILE Length(st) < 9 DO st := st+' ';
  445.       Write(st);
  446.  
  447.       {write the command line that invoked the program}
  448.       IF b = SumNum THEN
  449.         st := ''
  450.       ELSE
  451.         st := CommandLine(psp);
  452.       IF verbose THEN
  453.         StLen := 13
  454.       ELSE
  455.         StLen := 19;
  456.       IF Length(st) > StLen-3 THEN
  457.         st := Copy(st, 1, StLen-3)+'...'
  458.       ELSE
  459.         WHILE Length(st) < StLen DO st := st+' ';
  460.       Write(st, ' ');
  461.  
  462.       {write the trapped interrupt vectors}
  463.       IF b <> SumNum THEN WriteHooks(psp, psp+psplen);
  464.  
  465.       WriteLn;
  466.     END;
  467.  
  468.   END {showtheblocks} ;
  469.  
  470.   PROCEDURE ShowTheEMSblocks;
  471.     {-map out expanded memory, if present}
  472.   CONST
  473.     EMSinterrupt = $67;       {the vector used by the expanded memory manager}
  474.     MaxHandles = 255;
  475.  
  476.   TYPE
  477.     HandlePageRecord =
  478.     RECORD
  479.       handle : Integer;
  480.       numpages : Integer;
  481.     END;
  482.  
  483.     PageArray = ARRAY[0..MaxHandles] OF HandlePageRecord;
  484.     PageArrayPtr = ^PageArray;
  485.  
  486.   VAR
  487.     EMSregs : registers;
  488.     EMShandles : Integer;
  489.     Map : PageArrayPtr;
  490.     TotalPages : Integer;
  491.  
  492.     FUNCTION EMSpresent : Boolean;
  493.       {-return true if EMS memory manager is present}
  494.     VAR
  495.       f : FILE;
  496.     BEGIN
  497.       {"file handle" defined by the expanded memory manager at installation}
  498.       Assign(f, 'EMMXXXX0');
  499.       {$I-} Reset(f) {$I+} ;
  500.       EMSpresent := (IOResult = 0);
  501.       Close(f);
  502.     END {EMSpresent} ;
  503.  
  504.     FUNCTION EMSpagesAvailable(VAR TotalPages : Integer) : Integer;
  505.       {-return the number of 16K expanded memory pages available and unallocated}
  506.     BEGIN
  507.       EMSregs.ah := $42;
  508.       Intr(EMSinterrupt, EMSregs);
  509.       IF EMSregs.ah <> 0 THEN BEGIN
  510.         WriteLn('EMS device not responding');
  511.         EMSpagesAvailable := 0;
  512.         Exit;
  513.       END;
  514.       EMSpagesAvailable := EMSregs.bx;
  515.       TotalPages := EMSregs.dx;
  516.     END {EMSpagesAvailable} ;
  517.  
  518.     FUNCTION EMShandlesActive : Integer;
  519.       {-return the number of active EMS handles}
  520.     BEGIN
  521.       EMSregs.ah := $4B;
  522.       Intr(EMSinterrupt, EMSregs);
  523.       IF EMSregs.ah <> 0 THEN BEGIN
  524.         WriteLn('EMS device not responding');
  525.         EMShandlesActive := 0;
  526.         Exit;
  527.       END;
  528.       EMShandlesActive := EMSregs.bx;
  529.     END {EMShandlesActive} ;
  530.  
  531.     PROCEDURE EMSpageMap(VAR PageMap : PageArray);
  532.       {-return an array of the allocated memory blocks}
  533.     BEGIN
  534.       EMSregs.ah := $4D;
  535.       EMSregs.es := Seg(PageMap);
  536.       EMSregs.di := Ofs(PageMap);
  537.       EMSregs.bx := 0;
  538.       Intr(EMSinterrupt, EMSregs);
  539.       IF EMSregs.ah <> 0 THEN
  540.         WriteLn('EMS device not responding');
  541.     END {EMSpageMap} ;
  542.  
  543.     PROCEDURE WriteEMSmap(PageMap : PageArray; handles : Integer);
  544.       {-write out the EMS page map}
  545.     VAR
  546.       h : Integer;
  547.     BEGIN
  548.       WriteLn('block   bytes   (Expanded Memory)');
  549.       WriteLn('-----   ------');
  550.       FOR h := 0 TO Pred(handles) DO WITH PageMap[h] DO
  551.         WriteLn(h:5, '  ', (16384.0*Cardinal(numpages)):7:0);
  552.     END {writeEMSmap} ;
  553.  
  554.   BEGIN
  555.     IF NOT(EMSpresent) THEN Exit;
  556.     EMShandles := EMShandlesActive;
  557.     WriteLn;
  558.     GetMem(Map, 4*EMShandles);
  559.     EMSpageMap(Map^);
  560.     WriteEMSmap(Map^, EMShandles);
  561.     WriteLn(' free  ', (16384.0*Cardinal(EMSpagesAvailable(TotalPages))):7:0);
  562.     WriteLn('total  ', (16384.0*Cardinal(TotalPages)):7:0);
  563.   END {showtheemsblocks} ;
  564.  
  565.  
  566. BEGIN
  567.   verbose := False;
  568.   IF ParamCount > 0 THEN
  569.     IF (ParamStr(1) = 'V') OR (ParamStr(1) = 'v') THEN
  570.       verbose := True;
  571.   FindTheBlocks;
  572.   ShowTheBlocks;
  573.   ShowTheEMSblocks;
  574. END.
  575.